﻿//========================================================= 
/**@file namedpipe_svr.h
 * @brief 进程之间通过命名管道调用监控项的服务
 *
 * - 协议:
 *  - 1.通过json字符串进行通信
 *  - 2.包格式为 STX + json + ETX
 *  - 3.客户端(zabbix_agentd)发送字段为: Command(string),Arg(string)
 *  - 4.服务端返回字段为: CODE(int),RLT(string)
 *  - 5.服务端自动枚举程序当前目录下的监控插件,并根据Command自动调用
 * .
 * 
 * @date 2018-09-11   08:42:38
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_NAMEDPIPE_SVR_H_
#define _LIBZHOUYB_NAMEDPIPE_SVR_H_
//--------------------------------------------------------- 
#include "zabbix_converter.h"

#include "../../base_device/win32/NamedPipeDevice.h"
using zhou_yb::base_device::env_win32::NamedPipeDevice;

#include "../../device/cmd_adapter/CommandFilter.h"
using zhou_yb::device::cmd_adapter::CommandFilter;

#include "../rapidjson/rapidjson_extractor.h"
using zhou_yb::wrapper::rapidjson_extractor::RapidJsonArgParser;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace zabbix_extractor {
//--------------------------------------------------------- 
/// 管道名
#ifndef ZABBIX_NAMEDPIPE_NAME
#   define ZABBIX_NAMEDPIPE_NAME "Zabbix"
#endif
//--------------------------------------------------------- 
/**
 * @brief 管道通信指令适配器
 * @date 2018-09-11 14:39
 */
class namedpipe_cmdadapter
{
protected:
    Ref<IInteractiveTrans> _dev;
    ByteBuilder _sendBuff;
    ByteBuilder _recvBuff;
    CommandFilter _cmdFilter;
    RapidJsonArgParser _parser;
public:
    namedpipe_cmdadapter() {}
    namedpipe_cmdadapter(Ref<IInteractiveTrans> dev) { select(dev); }

    void select(Ref<IInteractiveTrans> dev)
    {
        _dev = dev;
        _cmdFilter.SelectDevice(_dev);
        _cmdFilter.SetReadTX(0x02, 0x03);
    }

    bool send(const IStringArguments& arg)
    {
        _sendBuff.Clear();
        _sendBuff += 0x02;
        _parser.ToString(arg, _sendBuff);
        _sendBuff += 0x03;
        return _dev->Write(_sendBuff);
    }
    bool recv(IStringArguments& rlt)
    {
        _recvBuff.Clear();
        bool bOK = _cmdFilter.Read(_recvBuff);
        if(bOK)
        {
            size_t count = _parser.Parse(_recvBuff.SubArray(1, _recvBuff.GetLength() - 2), rlt);
            bOK = (count > 0);
        }
        return bOK;
    }
    
    bool send(const string& data)
    {
        _sendBuff.Clear();
        _sendBuff += 0x02;
        _sendBuff.Append(ByteArray(data.c_str(), data.length()));
        _sendBuff += 0x03;
        return _dev->Write(_sendBuff);
    }
    bool recv(string& data)
    {
        _recvBuff.Clear();
        bool bOK = _cmdFilter.Read(_recvBuff);
        if(bOK)
        {
            data.append(_recvBuff.GetString(), _recvBuff.GetLength());
        }
        return bOK;
    }
};
//--------------------------------------------------------- 
/**
 * @brief 命名管道通信服务
 * @date 2018-09-11 14:38
 */
class namedpipe_svr : public converter_invoker
{
protected:
    int handle_command(Ref<IInteractiveTrans> dev)
    {
        namedpipe_cmdadapter adapter(dev);
        StringArguments a;
        if(!adapter.recv(a))
            return DeviceError::RecvErr;
        string cmd;
        string arg;
        if(!a.Get("Command", cmd) || !a.Get("Arg", arg))
            return DeviceError::RecvFormatErr;
        StringArguments r;
        string rlt;
        int err = on_command(cmd, arg, rlt);
        if(err == DeviceError::Success)
        {
            r.Put("RLT", rlt);
            r.Put("CODE", ArgConvert::ToString<int>(err));
            if(!adapter.send(r))
                return DeviceError::SendErr;
        }
        return err;
    }
public:
    namedpipe_svr() {}
    namedpipe_svr(const char* dir) { load(dir); }

    int run(uint timeoutMs = DEV_WAIT_TIMEOUT, 
        Ref<IInterrupter> interrupter = Ref<IInterrupter>())
    {
        NamedPipeDevice dev;
        if(!dev.Open(".", ZABBIX_NAMEDPIPE_NAME, 2000))
            return DeviceError::DevOpenErr;
        if(!dev.WaitConnect(timeoutMs, DEV_OPERATOR_INTERVAL, interrupter))
            return DeviceError::WaitTimeOutErr;
        return handle_command(dev);
    }
};
//--------------------------------------------------------- 
/**
 * @brief 使用命名管道进行通信的监控项
 * @date 2018-09-11 14:38
 */
class namedpipe_monitor
{
public:
    typedef string value_type;

    static value_type put_err(int err)
    {
        return ArgConvert::ToString<int>(-err);
    }

    static int run(const char* cmd, const char* arg, string& rlt, 
        uint timeoutMs = DEV_WAIT_TIMEOUT,
        uint intervalMs = DEV_OPERATOR_INTERVAL,
        Ref<IInterrupter> interrupter = Ref<IInterrupter>(),
        const char* namedpipe = ZABBIX_NAMEDPIPE_NAME)
    {
        string pipeName = NamedPipeDevice::Name(".", namedpipe);
        FileDevice dev;
        DeviceError::ErrorCode errWait = NamedPipeDevice::Wait(dev, pipeName.c_str(), timeoutMs, intervalMs, interrupter);
        if(errWait != DeviceError::Success) return DeviceError::DevConnectErr;

        namedpipe_cmdadapter adapter(dev);
        StringArguments a;
        StringArguments r;
        a.Put("Command", _strput(cmd));
        a.Put("Arg", _strput(arg));
        if(!adapter.send(a) || !adapter.recv(r))
            return DeviceError::TransceiveErr;
        int err = r["CODE"].To<int>(DeviceError::Success);
        if(err != DeviceError::Success)
            return err;
        rlt = r["RLT"].To<string>();
        return DeviceError::Success;
    }

    static value_type execute(int argc, const char* argv[])
    {
        if(argc < 1) return put_err(DeviceError::ArgLengthErr);
        const char* cmd = argv[1];
        string arg = "";
        for(int i = 2; i < argc; ++i)
        {
            arg += argv[i];
            arg += ' ';
        }
        if(argc > 2) arg.pop_back();
        string rlt = "";
        int err = run(cmd, arg.c_str(), rlt);
        if(err == DeviceError::Success)
            return rlt;
        return put_err(err);
    }
};
//--------------------------------------------------------- 
} // namespace zabbix_extractor 
} // namespace wrapper
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_NAMEDPIPE_SVR_H_
//========================================================= 